package com.sinodata.bsm.common.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.RandomAccessFile;
import java.io.Serializable;
import java.util.Properties;

import org.apache.log4j.Logger;
import org.apache.oro.text.regex.MatchResult;
import org.apache.oro.text.regex.Pattern;
import org.apache.oro.text.regex.PatternCompiler;
import org.apache.oro.text.regex.PatternMatcher;
import org.apache.oro.text.regex.Perl5Compiler;
import org.apache.oro.text.regex.Perl5Matcher;

/**
 * 
 * <p>
 * Description: 文件工具类
 * </p>
 *
 * @author wangyazhou
 * @version 1.0

 * <p>
 * History: 
 *
 * Date                     Author         Version     Description
 * ---------------------------------------------------------------------------------
 * 2012-8-3 下午8:21:25          wangyazhou        1.0         To create
 * </p>
 *
 * @since 
 * @see
 */
public class FileUtil {
    private static final Logger logger = Logger.getLogger(FileUtil.class);
    private static String FILE_PATH_SEPERATOR = null;

    /**
     * 获取文件路径名的分隔符。在unix系统上将返回"/",在windows上返回"\"
     * @author zhaoch
     * @return 文件路径的分隔符
     */
    public static String getFilePathSeperator() {
        if (FILE_PATH_SEPERATOR == null) {
            FILE_PATH_SEPERATOR = System.getProperty("file.separator");
        }
        if (FILE_PATH_SEPERATOR == null) {
            FILE_PATH_SEPERATOR = "/";
        }
        return FILE_PATH_SEPERATOR;
    }

    /**
     * 从文件中读取对象
     * @param filename 读取对象的文件名
     * @return 从文件中读取的对象
     * @author zhaoch
     */
    public static Object readObjFromFile(String filename) {
        Object obj = null;
        ObjectInputStream ois = null;
        FileInputStream fis = null;

        try {
            fis = new FileInputStream(filename);
            ois = new ObjectInputStream(fis);
            obj = ois.readObject();
            return obj;
        } catch (FileNotFoundException fne) {
            return null;
        } catch (Exception ex) {
            Logger.getLogger(FileUtil.class.getName()).error("when read object from " + filename, ex);
            return null;
        } finally {
            try {
                if (ois != null) {
                    ois.close();
                }
            } catch (IOException ie) {
            }
        }
    }

    /**
     * 强制将一个对象写入文件。如果文件不存在则先创建文件
     * @param filename 进行写的文件名
     * @param obj 要写入的对象
     * @param append 是否追加。否，则覆盖;是，则追加
     * @throws IOException
     * @author zhaoch
     */
    public static void forceWriteObjToFile(String filename, Object obj, boolean append) throws IOException {
        File file = new File(filename);
        ObjectOutputStream oos = null;
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(file);
            oos = new java.io.ObjectOutputStream(fos);
            if (file.exists()) {
                oos.writeObject(obj);
            } else {
                boolean fileFlag = file.createNewFile();
                if (fileFlag) {
                    oos.writeObject(obj);
                } else {
                    logger.error("Failed to create file!");
                }
            }
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (Throwable t) {
                }
            }
            if (oos != null) {
                try {
                    oos.reset();
                    oos.close();
                } catch (Throwable t) {
                }
            }
        }
    }

    /**
     * 从一个文件中读出保存的时间long数据
     * @param filename 保存时间的文件名
     * @return 文件中保存的时间
     */
    public static long getLongFormFile(String filename) {
        FilePosition pos = (FilePosition) getPositionFormFile(filename);
        if (pos == null) {
            return -1;
        } else {
            return pos.getLongData();
        }
    }

    /**
     * 将long数据写入指定的文件中
     * @param filename 进行写的文件名
     * @param l 要写入的long类型数据
     * @return 写入成功返回true，否则返回false
     */
    public static boolean setLongToFile(String filename, long l) {
        FilePosition pos = new FilePosition();
        pos.setLongData(l);
        if (setPositionToFile(filename, pos)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 从文件读取一个Serializable对象
     * @param filename 读的文件名
     * @return Serializable对象
     */
    public static Serializable getPositionFormFile(String filename) {
        File posFile = null;
        ObjectInputStream in = null;
        try {
            posFile = new File(filename);
            if (posFile.exists()) {
                in = new ObjectInputStream(new FileInputStream(posFile));
                return (Serializable) in.readObject();
            }
        } catch (Throwable t) {
            return null;
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException ex) {
                }
            }
        }
        return null;
    }

    /**
     * 将一个Serializable对象保存到文件中
     * @param filename 保存对象的文件名
     * @param pos 要保存的Serializable对象
     * @return 保存成功返回true，否则返回false
     */
    public static boolean setPositionToFile(String filename, Serializable pos) {
        File posFile = new File(filename);
        if (!posFile.exists()) {
            try {
                posFile.createNewFile();
            } catch (IOException ex2) {
                return false;
            }
        }
        ObjectOutputStream out = null;
        try {
            out = new ObjectOutputStream(new FileOutputStream(posFile));
            out.writeObject(pos);
        } catch (IOException ex) {
            ex.printStackTrace();
            return false;
        } finally {
            if (out != null) {
                try {
                    out.reset();
                    out.close();
                } catch (IOException ex1) {
                }
            }
        }
        return true;
    }

    /**
     * 从一个文件中采集数据
     * 该方法从上次采集的结束位置开始直到文件结尾,采集满足匹配式的字符串
     * 如果记录上次采集位置的临时文件没找到,则这次采集返回null,记录文件的总长度为本次采集的位置
     * 如果文件的长度小于上次采集的位置,则这次采集返回null,记录文件的总长度为本次采集的位置
     * 一次采集可能返回多个值,之间用sepvalue分隔
     * 对于一个值返回正则表达式中的group,每个group中间用sepgroup分隔
     * @param filename 采集数据的文件名
     * @param tmpfile 记录文件位置的临时文件名
     * @param match 正则匹配表达式
     * @param sepgroup 返回的一条采集数据中分隔符
     * @param sepvalue 返回的多条采集数据之间的分隔符
     * @return
     */
    public static String collectFromFile(String filename, String tmpfile, String match, String sepgroup, String sepvalue) {
        RandomAccessFile file = null;
        long length;
        long filelength = -1;
        String value = null;
        length = getLongFormFile(tmpfile);
        if (length > 0) {
            value = collectFromFileBeginLine(filename, length, match, sepgroup, sepvalue);
        }
        try {
            file = new RandomAccessFile(filename, "r");
            filelength = file.length();
        } catch (Exception ex) {
            return null;
        } finally {
            if (filelength > 0) {
                setLongToFile(tmpfile, filelength);
            }
            if (file != null) {
                try {
                    file.close();
                } catch (IOException ex1) {
                }
            }
        }
        return value;
    }

    /**
     * 从一个文件中的指定位置开始采集,返回采集的结果
     * 一次采集可能返回多个值,之间用sepvalue分隔
     * 对于一个值返回正则表达式中的group,每个group中间用sepgroup分隔
     * @param filename 文件名
     * @param line 采集的位置
     * @param match 正则匹配表达式
     * @param sepgroup 返回的一条采集数据中分隔符
     * @param sepvalue 返回的多条采集数据之间的分隔符
     * @return
     */
    public static String collectFromFileBeginLine(String filename, long line, String match, String sepgroup, String sepvalue) {
        RandomAccessFile file = null;
        long filelength;
        try {
            file = new RandomAccessFile(filename, "r");
            filelength = file.length();
        } catch (Exception ex) {
            closeRandomAccessFile(file);
            return null;
        }
        int lines = getLinesFromExp(match);
        String[] readLines = new String[lines];
        StringBuilder matchLine = new StringBuilder();
        if (line >= filelength) {
            closeRandomAccessFile(file);
            return null;
        }
        try {
            file.seek(line);
        } catch (IOException ex1) {
            closeRandomAccessFile(file);
            return null;
        }
        try {
            String strTmp = null;
            int index = 0;
            while (index < readLines.length) {
                strTmp = file.readLine();
                if (strTmp == null) {
                    return null;
                }
                if (strTmp.trim().length() == 0) {
                    continue;
                }
                readLines[index++] = strTmp;
            }
        } catch (Exception e) {
            closeRandomAccessFile(file);
            return null;
        }
        String result = null;
        MatchResult matchResult = null;
        PatternMatcher matcher = new Perl5Matcher();
        PatternCompiler compiler = new Perl5Compiler();
        try {
            while (true) {
                matchLine.append(readLines[0]);
                for (int i = 1; i < readLines.length; i++) {
                    matchLine.append('\n').append(readLines[i]);
                }
                Pattern patten = compiler.compile(match);
                if (matcher.contains(matchLine.toString(), patten)) {
                    matcher.matches(matchLine.toString(), patten);
                    matchResult = matcher.getMatch();
                    String values = null;
                    for (int i = 0; i < matchResult.groups(); i++) {
                        if (values == null) {
                            values = matchResult.group(i);
                        } else {
                            values += sepgroup + matchResult.group(i);
                        }
                    }
                    if (result == null) {
                        result = values;
                    } else {
                        result += sepvalue + values;
                    }
                }
                for (int i = 0; i < readLines.length - 1; i++) {
                    readLines[i] = readLines[i + 1];
                }
                String strTmp = null;
                while (true) {
                    strTmp = file.readLine();
                    if (strTmp == null || strTmp.trim().length() > 0) {
                        break;
                    }
                }
                if (strTmp == null) {
                    break;
                }
                readLines[readLines.length - 1] = strTmp;
            }
        } catch (Throwable t) {
        } finally {
            closeRandomAccessFile(file);
        }
        return result;
    }

    /**
     * 返回一个配置文件的Properties
     * 在出错时返回空
     * @param filename Properties文件名
     * @return Properties对象
     */
    public static Properties getPropertiesFromFile(String filename) {
        Properties prop = null;
        File configFile = null;
        FileInputStream configStream = null;
        prop = new Properties();
        configFile = new File(filename);
        try {
            configStream = new FileInputStream(configFile);
            prop.load(configStream);
        } catch (Exception e) {
            return null;
        } finally {
            if (configStream != null) {
                try {
                    configStream.close();
                } catch (IOException ex) {
                }
            }
        }

        return prop;
    }

    /**
     * 关闭一个RandomAccessFile文件符
     * @param file RandomAccessFile 要关闭的文家符
     */
    public static void closeRandomAccessFile(RandomAccessFile file) {
        if (file != null) {
            try {
                file.close();
            } catch (IOException ex) {
            }
        }
    }

    /**
     * 根据正则表达式算出需要匹配的行数
     * @param exp 原始字符串
     * @return int 行数
     */
    private static int getLinesFromExp(String exp) {
        return StringUtil.containSubstring(exp, "\\n") + 1;
    }

    /**
     * 向一个文件中追加字符串内容
     * @param log String 要追加的字符串
     * @param fileName String 要写入的文件名
     */
    public static void appendLog(String log, String fileName) {
        RandomAccessFile file = null;
        try {
            file = new RandomAccessFile(fileName, "rw");
            file.seek(file.length());
            file.writeBytes(log + "\n");
        } catch (Exception e) {
            logger.error("Failed to append the string to log", e);
        } finally {
            closeRandomAccessFile(file);
        }
    }

    /**
     * 验证一个字符串为有效文件名，如果存在文件名中不允许的字符，将不允许的字符去掉
     * @param fileName String 原始字符串
     * @return String 有效的文件名
     */
    public static String validateFileName(String fileName) {
        char[] ilChar = new char[] { '\\', '/', ':', '*', '?', ':', '<', '>', '|' };
        StringBuffer buff = new StringBuffer();
        for (int i = 0; i < fileName.length(); i++) {
            char ch = fileName.charAt(i);
            if (!isInArr(ch, ilChar)) {
                buff.append(ch);
            }
        }
        return buff.toString();
    }

    private static boolean isInArr(char ch, char[] arr) {
        for (int i = 0; i < arr.length; i++) {
            if (ch == arr[i]) {
                return true;
            }
        }
        return false;
    }

    /**
     * 文件中的内容结构
     */
    public static class FilePosition implements java.io.Serializable {
        /**
         *
         */
        private static final long serialVersionUID = 2429782519099469461L;
        private long pointer;

        public long getLongData() {
            return pointer;
        }

        public void setLongData(long l) {
            pointer = l;
        }
    }

}
